#include "main.h"

JSRuntime *rt = NULL;
JSContext *cx = NULL;

JSObject *get_global_object() {
	return JS_GetGlobalObject(cx);
}


char *json_encode(JSContext *cx, jsval x) {
	jsval json;
	JS_GetProperty(cx, get_global_object(), "JSON", &json);
	assert(JSVAL_IS_OBJECT(json));
	jsval args[1];
	args[0] = x;
	jsval outval;
	if (!JS_CallFunctionName(cx, JSVAL_TO_OBJECT(json), "stringify", 1, args, &outval))
		return NULL;
	return vtoa(cx, outval);
}


JSBool json_decode(JSContext *cx, char *str, jsval *rval) {
	jsval json;
	JS_GetProperty(cx, get_global_object(), "JSON", &json);
	assert(JSVAL_IS_OBJECT(json));
	jsval args[1];
	args[0] = atov(cx, str);
	jsval outval;
	return JS_CallFunctionName(cx, JSVAL_TO_OBJECT(json), "parse", 1, args, rval);
}



void wakeup(int i) {
	signal(SIGALRM, wakeup);
	
	//int i = 0;
	//console_set_lock(1);
	//printf("wakeup!\n");
	//console_set_lock(0);

	struct itimerval tout_val;
	tout_val.it_interval.tv_sec = 0;
	tout_val.it_interval.tv_usec = 0;
	tout_val.it_value.tv_sec = 1;
	tout_val.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL, &tout_val, 0);
	JS_TriggerAllOperationCallbacks(rt);
}

void exit_func(int i) {
	signal(SIGINT, exit_func);
	console_set_lock(1);
	printf("exit\n");
	console_set_lock(0);
	//exit(0);
}




void class_global_init(JSContext *cx);



void execute(void(*proc)(void *), void *arg, int async) {
	trace1("execute");
	if (async)
		PR_CreateThread(PR_USER_THREAD, proc, arg, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,  PR_JOINABLE_THREAD, 0);
	else
		proc(arg);
}



char * _js_convert_arg_to_str(JSContext *cx, jsval *vp, int n) {
	char *s;
	if (!JS_ConvertArguments(cx, 1, JS_ARGV(cx, vp) + n, "s", &s)) {
		JS_ReportError(cx, "Cannot convert parameter %d to string", n);
		return NULL;
	}
	return s;
}

long _js_convert_arg_to_long(JSContext *cx, jsval *vp, int n) {
	long x;
	if (!JS_ConvertArguments(cx, 1, JS_ARGV(cx, vp) + n, "i", &x)) {
		JS_ReportError(cx, "Cannot convert parameter %d to long", n);
		return 0;
	}
	return x;
}

long _js_convert_arg_to_int(JSContext *cx, jsval *vp, int n) {
	int x;
	if (!JS_ConvertArguments(cx, 1, JS_ARGV(cx, vp) + n, "i", &x)) {
		JS_ReportError(cx, "Cannot convert parameter %d to int", n);
		return 0;
	}
	return x;
}


JSContext *create_context(JSContext *parent_cx) {
	trace1("create_context");
	trace5("call");
	
	JSContext *cx = JS_NewContext(rt, 16384);
	if (!cx)
		fatal("JS_NewContext()");
JS_BeginRequest(cx);
	//JS_SetOptions(cx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_VAROBJFIX);
	//JS_SetOptions(cx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_VAROBJFIX | JSOPTION_COMPILE_N_GO | JSOPTION_ATLINE);
	JS_SetOptions(cx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_COMPILE_N_GO | JSOPTION_ATLINE | JSOPTION_JIT);
//	JS_SetErrorReporter(cx, reportError);
//	class_global_init(cx);

	
	trace5("setting global object for new context");
	if (parent_cx)
		JS_SetGlobalObject(cx, JS_GetGlobalObject(parent_cx));
	else
		JS_SetGlobalObject(cx, get_global_object());
		
		
JS_EndRequest(cx);

	//JS_SetScriptStackQuota(cx, 16*1024*1024);
	return cx;
}

void destroy_context(JSContext *cx) {
	JS_DestroyContext(cx);
}



/**
 *
 */
void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
	if(report->filename)
		fprintf(stderr, "%s:%u: %s\n", report->filename, (unsigned int) report->lineno, message);
	else
		fprintf(stderr, "jshost: %s\n", message);
}


/**
 *
 */
char *vtoa(JSContext *cx, jsval s) {
	//return JS_GetStringBytes(JS_ValueToString(cx, s));
	return JS_EncodeString(cx, JS_ValueToString(cx, s));
}


/**
 *
 */
jsval atov(JSContext *cx, const char *a){
	return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, a)); 
}


/**
 *
 */
const char *get_prop(JSContext *cx, jsval v, const char *propname){
	jsval x;
	if(!JSVAL_IS_OBJECT(v)) return NULL;	
	if(!JS_GetProperty(cx, JSVAL_TO_OBJECT(v), propname, &x)) return TEXT_UNDEFINED;
	return vtoa(cx, x);
}


/**
 *
 */
JSBool c2jsarray(JSContext *cx, uintN len, char **argv, jsval *rval) {
	jsval *vec = (jsval *)malloc(len * sizeof(jsval));
	int i;
	for(i=0; i<len; ++i)
		vec[i] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, argv[i]));
	*rval = OBJECT_TO_JSVAL(JS_NewArrayObject(cx, len, vec));
	return JS_TRUE;
}


/**
 *
 */
const char *geta(JSContext *cx, JSObject *ar, int n, const char *def){
	jsval x;	
	JS_GetElement(cx, ar, n, &x);
	if (JSVAL_IS_STRING(x)) {
		//return JS_GetStringBytes(JSVAL_TO_STRING(x));
		return JS_EncodeString(cx, JSVAL_TO_STRING(x));
	}
	return def;
}


/**
 *
 */
int geti(JSContext *cx, JSObject *ar, int index, int _default){
	jsval v;
	int i;
	if(!ar) return _default;
	if(!JS_IsArrayObject(cx, ar)) return _default;
	if(!JS_GetElement(cx, ar, index, &v)) return _default;
	if(!JSVAL_IS_STRING(v)) return _default;
	if(!JS_ValueToInt32(cx, v, &i)) return _default;
	return i;
}




#include "classes/classes.h"

JSGCCallback js_orig_gc_callback;

/**
 *
 */
JSBool
js_gc_callback(JSContext *cx, JSGCStatus status) {
	//jsrefcount rc = JS_SuspendRequest(cx);	
	trace1("js_gc_callback");
	if (status == JSGC_END)
		trace3("JSGC_END");
	//JS_ResumeRequest(cx, rc);
	//js_thread_t *x = (js_thread_t *)JS_GetContextPrivate(cx);
	//if(x)
	//	trace2("thread id %s", x->id);
	//else
	//	trace2("no thread id");
	//return js_orig_gc_callback(cx, status);
	return JS_TRUE;
}


void dom_init(JSContext *);





/**
 *
 */
JSContext *js_init() {
	trace1("js_init");
	static JSContext *init_result = NULL;
	if (init_result) return init_result;



	//JSContext *cx;
	
	//int ncpus = PR_GetNumberOfProcessors();
	//trace2("ncpus=%i", ncpus);
	//PR_SetConcurrency(ncpus);
	
//	uint32 maxbytes = 1073741824;

	uint32 maxbytes = 1024ul*1024*1024;

	rt = JS_NewRuntime(maxbytes);
//	JS_SetGCParameter(rt, JSGC_TRIGGER_FACTOR, 100000000);
	
/*	
	gcx = JS_NewContext(rt, 8192);
	JS_SetOptions(gcx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_COMPILE_N_GO | JSOPTION_ATLINE | JSOPTION_JIT);
	JS_SetErrorReporter(gcx, reportError);
*/	

	
	cx = JS_NewContext(rt, 8192);

#if VERBOSE_LEVEL >= 3	
	js_orig_gc_callback = JS_SetGCCallbackRT(rt, js_gc_callback);
#endif
	
	
	//init_result = cx;

	JS_BeginRequest(cx);
	
	//JS_SetOptions(cx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_VAROBJFIX | JSOPTION_COMPILE_N_GO | JSOPTION_ATLINE);
	JS_SetOptions(cx, JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_COMPILE_N_GO | JSOPTION_ATLINE | JSOPTION_JIT);
	JS_SetErrorReporter(cx, reportError);


	JSObject *global;
	
	class_global_init(cx);
	class_server_init(cx);
	//class_test_init(cx);
	//class_http_init(cx);
	class_stream_init(cx);
	
	class_curl_init(cx);

	//JS_EndRequest(cx);


	struct itimerval tout_val;
	tout_val.it_interval.tv_sec = 0;
	tout_val.it_interval.tv_usec = 0;
	tout_val.it_value.tv_sec = 1;
	tout_val.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL, &tout_val, 0);
	signal(SIGALRM, wakeup);	
	//signal(SIGINT, exit_func);
	
	
	return cx;
	
//	PR_NewThreadPrivateIndex(&prIndex, NULL);
}



/**
 *
 */
/*
void js_done(int ret){
	trace1("js_done");
//	JS_EndRequest(cx);	
	JS_ClearContextThread(cx);
	JS_DestroyContext(cx);
	JS_DestroyRuntime(rt);
	JS_ShutDown();
	
	//pth_kill();
	
	exit(ret);
}
*/











